package org.jfonia.pitch;


/**
 * Musical interval or pitch, specified by rank and modifier.
 * An Interval can be regarded as a musical interval, e.g. rank=THIRD and modifier=MAJOR
 * but also as a pitch, e.g. rank=G and modifier=SHARP.
 * <p>
 * The equivalence of intervals and pitches can be understood by regarding every pitch
 * as spanning an interval starting from a "zero" pitch, which we take to be middle C
 * (C4 = the C below the staff in G-clef). This is analogous to points in space, which are
 * considered to be equivalent to a vectors from the origin.
 * <p>
 * The advantage of using intervals is the ability to add and subtract them (just like vectors)
 * <p>
 *
 *
 * @author Wijnand
 *
 */
public final class MusicalInterval {
	private static final int[] semitones = {0,2,4,5,7,9,11};

	private int rank;	// diatonic rank (C=7000)
	private int rank12;	// midi-rank (C=12000)



	public static final int PERFECT = 0;
	public static final int MAJOR = 0;
	public static final int MINOR = -1;
	public static final int AUGMENTED = 1;
	/**
	 * for second, third, sixth and seventh.
	 */
	public static final int DIMINISHED_MODAL = -2;
	/**
	 * for unison, fourth, fifth, (octave...)
	 */
	public static final int DIMINISHED_TONAL = -1;

	public static final int UNISON = 0;
	public static final int SECOND = 1;
	public static final int THIRD = 2;
	public static final int FOURTH = 3;
	public static final int FIFTH = 4;
	public static final int SIXTH = 5;
	public static final int SEVENTH = 6;
	public static final int OCTAVE = 7;

	//
	public static final MusicalInterval PERRECT_UNISON = new MusicalInterval(UNISON, PERFECT);
	public static final MusicalInterval MINOR_SECOND = new MusicalInterval(SECOND, MINOR);
	public static final MusicalInterval MAJOR_SECOND = new MusicalInterval(SECOND, MAJOR);
	public static final MusicalInterval MINOR_THIRD = new MusicalInterval(THIRD, MINOR);
	public static final MusicalInterval MAJOR_THIRD = new MusicalInterval(THIRD, MAJOR);
	public static final MusicalInterval PERFECT_FOURTH = new MusicalInterval(FOURTH, PERFECT);
	public static final MusicalInterval PERFECT_FIFTH = new MusicalInterval(FIFTH, PERFECT);
	public static final MusicalInterval MINOR_SIXTH = new MusicalInterval(SIXTH, MINOR);
	public static final MusicalInterval MAJOR_SIXTH = new MusicalInterval(SIXTH, MAJOR);
	public static final MusicalInterval MINOR_SEVENTH = new MusicalInterval(SEVENTH, MINOR);
	public static final MusicalInterval MAJOR_SEVENTH = new MusicalInterval(SEVENTH, MAJOR);
	public static final MusicalInterval PERFECT_OCTAVE = new MusicalInterval(OCTAVE, PERFECT);

	// accidentals:
	public static final int NATURAL = 0;
	public static final int SHARP = 1;
	public static final int FLAT = -1;
	public static final int DOUBLE_SHARP = 2;
	public static final int DOUBLE_FLAT = -2;

	// pitch-classes:
	public static final int C = 0;
	public static final int D = 1;
	public static final int E = 2;
	public static final int F = 3;
	public static final int G = 4;
	public static final int A = 5;
	public static final int B = 6;


	public static final MusicalInterval MIDDLE_C = new MusicalInterval(0, 0);


	/**
	 * Constructs a new Pitch-object with given rank and modifier.
	 * @param rank
	 * @param accidental
	 */
	public MusicalInterval(int rank, int accidental)
	{
		 this.rank   = rank+7000;
		 this.rank12 = (this.rank)/7*12 + semitones[(this.rank)%7] + accidental;
	}

	public MusicalInterval(int rank12)
	{
		 this.rank12 = rank12 + 12000;
                 this.rank = this.rank12*7/12;
	}

	/**
	 * diatonic pitch.
	 *
	 * C=0  D=1  E=2,... c=7,...  B,=-1
	 */
	public int getRank()
	{
		return rank-7000;
	}

	/**
	 *  rank within current register (0..6), or "rank class".
	 */
	public int getRankClass()
	{
		return rank%7;
	}

	/**
	 *  register (octave)
	 *
	 *  C is in register 0, c in register 1, ...
	 */
	public int getRegister()
	{
		return rank/7-1000;
	}


	/*
	 * rank measured in semitones (well-tempered)
	 *
	 * C=0, CIS=DES=1, ..., c=12, ...
	 */
	public int getRank12()
	{
		return rank12 - 12000;
	}

	/*
	 * modifier determines accidental
	 * 0:none(natural), 1=sharp, -1:flat, 2:double sharp, -2:double flat
	 *
	 * note: visual accidental depends on context (signature, measure)!
	 */
	public int getAccidental()
	{
		return rank12 - semitones[rank%7] - rank/7*12;
	}


	public boolean equals(Object o)
	{
		if (!(o instanceof MusicalInterval))
			return false;
		MusicalInterval op = (MusicalInterval)o;
		return op.rank == rank && op.rank12 == rank12;
	}


	public String toString()
	{
		return "Interval[rank:" + getRank() + ",mod:" + getAccidental() +"]";
	}


}
